package dingtalkrobot

import (
	"fmt"
	"strings"

	jsoniter "github.com/json-iterator/go"
)

// iDingMsg iDingMsg
type iDingMsg interface {
	Marshaler() []byte
}

// AtOption atOption
type AtOption interface {
	apply(model *atModel)
}

// funcAtOption funcAtOption
type funcAtOption struct {
	f func(model *atModel)
}

// apply apply
func (fdo *funcAtOption) apply(do *atModel) {
	fdo.f(do)
}

// newFuncAtOption newFuncAtOption
func newFuncAtOption(f func(model *atModel)) *funcAtOption {
	return &funcAtOption{f: f}
}

// WithAtAll WithAtAll
func WithAtAll() AtOption {
	return newFuncAtOption(func(o *atModel) {
		o.IsAtAll = true
	})
}

// WithAtMobiles WithAtMobiles
func WithAtMobiles(mobiles []string) AtOption {
	return newFuncAtOption(func(o *atModel) {
		o.AtMobiles = mobiles
	})
}

// TextMsg TextMsg
type TextMsg struct {
	MsgType msgTypeType `json:"msgtype,omitempty"`
	Text    textModel   `json:"text,omitempty"`
	At      atModel     `json:"at,omitempty"`
}

// Marshaler Marshaler
func (t TextMsg) Marshaler() []byte {
	b, _ := jsoniter.Marshal(t)
	return b
}

// NewTextMsg NewTextMsg
func NewTextMsg(content string, opts ...AtOption) *TextMsg {
	msg := &TextMsg{MsgType: TEXT, Text: textModel{Content: content}}
	for _, opt := range opts {
		opt.apply(&msg.At)
	}
	return msg
}

// LinkMsg linkMsg
type LinkMsg struct {
	MsgType msgTypeType `json:"msgtype,omitempty"`
	Link    linkModel   `json:"link,omitempty"`
}

// Marshaler Marshaler
func (l LinkMsg) Marshaler() []byte {
	b, _ := jsoniter.Marshal(l)
	return b
}

// NewLinkMsg NewLinkMsg
func NewLinkMsg(title, text, picURL, msgURL string) *LinkMsg {
	return &LinkMsg{MsgType: LINK, Link: linkModel{
		Text:       text,
		Title:      title,
		PicURL:     picURL,
		MessageURL: msgURL,
	}}
}

// MarkDownMsg MarkDownMsg
type MarkDownMsg struct {
	MsgType  msgTypeType   `json:"msgtype,omitempty"`
	Markdown markDownModel `json:"markdown,omitempty"`
	At       atModel       `json:"at,omitempty"`
}

// Marshaler Marshaler
func (m MarkDownMsg) Marshaler() []byte {
	b, _ := jsoniter.Marshal(m)
	return b
}

// NewDTMDMsg NewDTMDMsg
func NewDTMDMsg(title string, dtmdMap *DingMap, opts ...AtOption) *MarkDownMsg {
	text := ""
	for _, v := range dtmdMap.l {
		text = text + "\n - " + fmt.Sprintf(dtmdFormat, v, dtmdMap.m[v])
	}
	return NewMarkDownMsg(title, text, opts...)
}

// NewMarkDownMsg NewMarkDownMsg
func NewMarkDownMsg(title string, text interface{}, opts ...AtOption) *MarkDownMsg {

	msg := &MarkDownMsg{MsgType: MARKDOWN, Markdown: markDownModel{Title: title, Text: text.(string)}}
	for _, opt := range opts {
		opt.apply(&msg.At)
	}
	// markdown格式需要在文本内写入被at的人
	if len(msg.At.AtMobiles) > 0 {
		var atStr = "\n -"
		for _, mobile := range msg.At.AtMobiles {
			atStr = atStr + " @" + mobile
		}
		msg.Markdown.Text = msg.Markdown.Text + atStr
	}
	return msg
}

// ActionCardOption ActionCardOption
type ActionCardOption interface {
	apply(model *actionCardModel)
}

// funcActionCardOption funcActionCardOption
type funcActionCardOption struct {
	f func(model *actionCardModel)
}

// apply apply
func (fdo *funcActionCardOption) apply(do *actionCardModel) {
	fdo.f(do)
}

// newFuncActionCardOption newFuncActionCardOption
func newFuncActionCardOption(f func(model *actionCardModel)) *funcActionCardOption {
	fa := &funcActionCardOption{f: f}
	return fa
}

// WithCardBtnVertical WithCardBtnVertical
func WithCardBtnVertical() ActionCardOption {
	a := newFuncActionCardOption(func(o *actionCardModel) {
		o.BtnOrientation = vertical
	})
	return a
}

// WithCardSingleTitle WithCardSingleTitle
func WithCardSingleTitle(title string) ActionCardOption {
	return newFuncActionCardOption(func(o *actionCardModel) {
		o.SingleTitle = title
	})
}

// WithCardSingleURL WithCardSingleURL
func WithCardSingleURL(url string) ActionCardOption {
	return newFuncActionCardOption(func(o *actionCardModel) {
		o.SingleURL = url
	})
}

// WithCardBtns WithCardBtns
func WithCardBtns(btns []ActionCardMultiBtnModel) ActionCardOption {
	return newFuncActionCardOption(func(o *actionCardModel) {
		o.Btns = btns
	})
}

// ActionCardMsg ActionCardMsg
type ActionCardMsg struct {
	MsgType    msgTypeType     `json:"msgtype,omitempty"`
	ActionCard actionCardModel `json:"actionCard,omitempty"`
}

// Marshaler Marshaler
func (a ActionCardMsg) Marshaler() []byte {
	b, _ := jsoniter.Marshal(a)
	return b
}

// NewActionCardMsg NewActionCardMsg
func NewActionCardMsg(title, text string, opts ...ActionCardOption) *ActionCardMsg {
	card := &ActionCardMsg{MsgType: ACTIONCARD, ActionCard: actionCardModel{
		Title:          title,
		Text:           text,
		BtnOrientation: horizontal,
	}}
	for _, opt := range opts {
		opt.apply(&card.ActionCard)
	}
	return card
}

// FeedCardMsg FeedCardMsg
type FeedCardMsg struct {
	MsgType  msgTypeType   `json:"msgtype,omitempty"`
	FeedCard feedCardModel `json:"feedCard,omitempty"`
}

// Marshaler Marshaler
func (f FeedCardMsg) Marshaler() []byte {
	b, _ := jsoniter.Marshal(f)
	return b
}

// NewFeedCardMsg NewFeedCardMsg
func NewFeedCardMsg(feedCard []FeedCardLinkModel) *FeedCardMsg {
	return &FeedCardMsg{MsgType: FEEDCARD, FeedCard: feedCardModel{Links: feedCard}}
}

// MarkType MarkType
type MarkType string

// DingMap 有序map
type DingMap struct {
	m map[string]MarkType
	l []string
}

// DingTalkMap DingMap
func DingTalkMap() *DingMap {
	d := &DingMap{m: make(map[string]MarkType), l: make([]string, 0, 0)}
	return d
}

// Set Set
func (d *DingMap) Set(val string, t MarkType) *DingMap {
	d.l = append(d.l, val)
	d.m[val] = t
	return d
}

// Remove Remove
func (d *DingMap) Remove(val string) {
	if _, ok := d.m[val]; ok {
		for i, v := range d.l {
			if v == val {
				d.l = append(d.l[:i], d.l[i+1:]...)
				break
			}
		}
		delete(d.m, val)
	}
}

// Slice Slice
func (d *DingMap) Slice() []string {
	resList := make([]string, 0, len(d.l))
	for _, val := range d.l {
		content := d.formatVal(val, d.m[val])
		resList = append(resList, content)
	}
	return resList
}

// formatVal formatVal
func (d *DingMap) formatVal(val string, t MarkType) (res string) {
	var ok bool
	if res, ok = hMap[t]; ok {
		vl := strings.Split(val, formatSpliter)
		if len(vl) == 3 {
			res = fmt.Sprintf(res, vl[1])
			res = vl[0] + res + vl[2]
		} else {
			res = fmt.Sprintf(res, val)
		}
	} else {
		res = val
	}
	if !strings.HasPrefix(res, "- ") && !strings.HasPrefix(res, "#") {
		res = "- " + res
	}
	return
}
